package com.olliebown.beads.data; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.Hashtable; import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.UnsupportedAudioFileException; import com.olliebown.beads.analysis.Frequency; import com.olliebown.beads.analysis.MelSpectrum; import com.olliebown.beads.analysis.OnsetDetector; import com.olliebown.beads.analysis.Power; import com.olliebown.beads.analysis.PowerSpectrum; import com.olliebown.beads.core.AudioContext; import com.olliebown.beads.core.AudioUtils; import com.olliebown.beads.events.AudioContextStopTrigger; import com.olliebown.beads.ugens.SamplePlayer; /** * A Sample is a buffer for audio data which can be loaded from a file or recorded into the buffer. * * @see SampleManager */ public class Sample { private String name; /** The audio format. */ public final AudioFormat audioFormat; /** The number of channels. */ public final int nChannels; /** The number of frames. */ public final int nFrames; public final float length; /** The audio data. */ public final float[][] buf; /** The result. */ private float[] result; private int featureHop; private Hashtable<String, float[][]> features; //time point hopCount, name, value /** * Instantiates a new empty buffer with the specified audio format and number of frames. * * @param audioFormat * the audio format * @param nFrames * the number frames */ public Sample(AudioFormat audioFormat, int nFrames) { this.audioFormat = audioFormat; nChannels = audioFormat.getChannels(); this.nFrames = nFrames; buf = new float[nChannels][nFrames]; for(int i = 0; i < nChannels; i++) { for(int j = 0; j < nFrames; j++) { buf[i][j] = 0.0f; } } result = new float[nChannels]; features = new Hashtable<String, float[][]>(); length = nFrames / audioFormat.getSampleRate() * 1000f; } /** * Creates a new Sample from the specified file. * * @param fn * the file name * * @throws UnsupportedAudioFileException * @throws IOException */ public Sample(String fn) throws UnsupportedAudioFileException, IOException { this.name = fn; File fileIn = new File(fn); AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(fileIn); audioFormat = audioInputStream.getFormat(); nChannels = audioFormat.getChannels(); nFrames = (int)audioInputStream.getFrameLength(); if(nFrames == -1) throw new IOException(); int frameHop = 256; byte[] audioBytes = new byte[frameHop * audioFormat.getFrameSize()]; buf = new float[nChannels][nFrames]; int framesRead = 0; int bytesRead; while ((bytesRead = audioInputStream.read(audioBytes)) != -1) { frameHop = bytesRead / audioFormat.getFrameSize(); float[] bufTemp = new float[frameHop * nChannels]; AudioUtils.byteToFloat(bufTemp, audioBytes, audioFormat.isBigEndian()); float[][] bufSegment = deinterleave(bufTemp); for (int i = 0; i < bufSegment.length; i++) { for (int j = 0; j < bufSegment[i].length; j++) { buf[i][j + framesRead] = bufSegment[i][j]; } } framesRead += frameHop; } audioInputStream.close(); result = new float[nChannels]; features = new Hashtable<String, float[][]>(); length = nFrames / audioFormat.getSampleRate() * 1000f; } public void loadFeatures() { //try to get the features try { readFeatures(); } catch (Exception e) { // do nothing } if(features == null || features.size() == 0) { extractFeatures(); } } public float[] getFeatures(String name, float time) { int featureIndex = msTimeToFeatureIndex(time); return features.get(name)[featureIndex]; } public void addFeatures(String name, float time, float values[]) { int timeIndex = msTimeToFeatureIndex(time); features.get(name)[timeIndex] = values; } public Integer msTimeToFeatureIndex(float time) { return new Integer((int)(msToSamples(time)) / featureHop); } /** * Deinterleaves an interleaved array. * * @param source * the interleaved array * * @return the de-interleaved array (channels x frames) */ private float[][] deinterleave(float[] source) { int nFrames = source.length / nChannels; float[][] result = new float[nChannels][nFrames]; for(int i = 0, count = 0; i < nFrames; i++) { for(int j = 0; j < nChannels; j++) { result[j][i] = source[count++]; } } return result; } /** * Interleaves a de-interleaved (channels x frames) array. * * @param source * the source array * * @return the interleaved array (frame by frame, alternating channels) */ private float[] interleave(float[][] source) { float[] result = new float[nChannels * nFrames]; for(int i = 0, counter = 0; i < nFrames; i++) { for(int j = 0; j < nChannels; j++) { result[counter++] = source[j][i]; } } return result; } /** * Converts from millieconds to samples. * * @param msTime * the time in milliseconds * * @return the time in samples */ public float msToSamples(float msTime) { return msTime * audioFormat.getSampleRate() / 1000.0f; } /** * Converts from samples to milliseconds. * * @param sampleTime * the time in samples * * @return the time in milliseconds */ public float samplesToMs(float sampleTime) { return sampleTime / audioFormat.getSampleRate() * 1000.0f; } /** * Retrieves a frame of audio using linear interpolation. * * @param currentSample * the current sample * @param fractionOffset * the offset from the current sample as a fraction of the time to the next sample * * @return the interpolated frame */ public float[] getFrameLinear(int currentSample, float fractionOffset) { if(currentSample >= 0 && currentSample < nFrames) { for (int i = 0; i < nChannels; i++) { if(currentSample < (nFrames - 1)) { result[i] = (1f - fractionOffset) * buf[i][currentSample] + fractionOffset * buf[i][currentSample + 1]; } else { result[i] = buf[i][currentSample]; } } } else { for(int i = 0; i < nChannels; i++) { result[i] = 0.0f; } } return result; } /** * Retrieves a frame of audio using cubic interpolation. * * @param currentSample * the current sample * @param fractionOffset * the offset from the current sample as a fraction of the time to the next sample * * @return the interpolated frame */ public float[] getFrameCubic(int currentSample, float fractionOffset) { float[] result = new float[nChannels]; float a0,a1,a2,a3,mu2; float ym1,y0,y1,y2; for (int i = 0; i < nChannels; i++) { int realCurrentSample = currentSample; if(realCurrentSample >= 0 && realCurrentSample < (nFrames - 1)) { realCurrentSample--; if (realCurrentSample < 0) { ym1 = buf[i][0]; realCurrentSample = 0; } else { ym1 = buf[i][realCurrentSample++]; } y0 = buf[i][realCurrentSample++]; if (realCurrentSample >= nFrames) { y1 = buf[i][nFrames - 1]; } else { y1 = buf[i][realCurrentSample++]; } if (realCurrentSample >= nFrames) { y2 = buf[i][nFrames - 1]; } else { y2 = buf[i][realCurrentSample]; } mu2 = fractionOffset * fractionOffset; a0 = y2 - y1 - ym1 + y0; a1 = ym1 - y0 - a0; a2 = y1 - ym1; a3 = y0; result[i] = a0 * fractionOffset * mu2 + a1 * mu2 + a2 * fractionOffset + a3; } else { result[i] = 0.0f; } } return result; } /** * Post audio format info. */ public void postAudioFormatInfo() { System.out.println("Sample Rate: " + audioFormat.getSampleRate()); System.out.println("Channels: " + nChannels); System.out.println("Frame size in Bytes: " + audioFormat.getFrameSize()); System.out.println("Encoding: " + audioFormat.getEncoding()); System.out.println("Big Endian: " + audioFormat.isBigEndian()); } /** * Write to a file. * * @param fn * the file name * * @throws IOException */ public void write(String fn) throws IOException { byte[] bytes = new byte[nFrames * audioFormat.getFrameSize()]; AudioUtils.floatToByte(bytes, interleave(buf), audioFormat.isBigEndian()); ByteArrayInputStream bais = new ByteArrayInputStream(bytes); AudioInputStream aos = new AudioInputStream(bais, audioFormat, nFrames); AudioSystem.write(aos, AudioFileFormat.Type.AIFF, new File(fn)); } public void extractFeatures() { featureHop = 1024; //set up the basic network AudioContext ac = new AudioContext(featureHop, -1, audioFormat); ac.setRealTime(false); SamplePlayer sp = new SamplePlayer(ac, this); sp.setEndListener(new AudioContextStopTrigger(ac)); PowerSpectrum ps = new PowerSpectrum(ac); ps.addInput(sp); sp.addDependent(ps); ps.setNumFeatures(10); ac.getRoot().addInput(sp); ps.accumulate(true); //run and extract ac.logTime(true); ac.runNonRealTime(); System.out.println("finished analysis"); float[][] allFeatures = new float[ps.getAccumulatedFeatures().size()][]; int totalFeatures = 10; for(int i = 0; i < ps.getAccumulatedFeatures().size(); i++) { float[] newFeatureFrame = new float[totalFeatures]; int featureIndex = 0; float[] powerBin = ps.getAccumulatedFeatures().get(i); for(int j = 0; j < powerBin.length; j++) { newFeatureFrame[featureIndex++] = powerBin[j]; } allFeatures[i] = newFeatureFrame; } features.put("all", allFeatures); if(name != null) { try { writeFeatures(); } catch(Exception e) { e.printStackTrace(); } } } public void writeFeatures(String filename) throws IOException { FileOutputStream fos = new FileOutputStream(filename); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(new Integer(featureHop)); oos.writeObject(features); oos.close(); fos.close(); } public void readFeatures(String filename) throws IOException, ClassNotFoundException { FileInputStream fis = new FileInputStream(filename); ObjectInputStream ois = new ObjectInputStream(fis); featureHop = (Integer)ois.readObject(); features = (Hashtable<String, float[][]>)ois.readObject(); ois.close(); fis.close(); } public void readFeatures() throws IOException, ClassNotFoundException { readFeatures(name + ".features"); } public void writeFeatures() throws IOException { writeFeatures(name + ".features"); } public void printFeatures(String name, String filename) throws IOException { File outFile = new File(filename); FileOutputStream fos = new FileOutputStream(outFile); PrintStream ps = new PrintStream(fos); float[][] theseFeatures = features.get(name); for(int i = 0; i < theseFeatures.length; i++) { for(int j = 0; j < theseFeatures[i].length; j++) { ps.println(i + " " + j + " " + theseFeatures[i][j]); } ps.println(); } ps.close(); fos.close(); } public String toString() { return name; } public static void main(String[] args) throws Exception { Sample s1 = new Sample("/Users/ollie/Music/Audio/output5.aif"); // Sample s1 = new Sample("audio/1234.aif"); s1.loadFeatures(); try { s1.printFeatures("all", "/Users/ollie/Desktop/audioFeatures"); } catch (IOException e) { e.printStackTrace(); } } }